package edu.vt.ece.rt.benchmark.vacation;

import java.util.ArrayList;
import java.util.Random;

import edu.vt.ece.rt.benchmark.BenchmarkThread;
import edu.vt.ece.rt.dstm.Context;
import edu.vt.ece.rt.dstm.ContextDelegator;
import edu.vt.ece.rt.dstm.TMProxy;
import edu.vt.ece.rt.quorum.ClustersManager;
import edu.vt.ece.rt.quorum.util.AbortException;
import edu.vt.ece.rt.util.Logger.Logger;

public class Vacation {

	static int NUM_RESERVATION_TYPE = 3;

	public static void makeReservation(String customerId, ArrayList<String> objIds, boolean nesting) {
		Context parentContext = null;
		Long priority = System.currentTimeMillis();

		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsRoot(i);
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("starting root transaction for check reservation");
			context.sanityNested();
			context.setPriority(priority);


			if (!nesting) {
				Random r = new Random();

				try {
					int sleepTime = r.nextInt(100)*(ClustersManager.baseClusterId + 1);
					Thread.sleep(sleepTime);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

			}

			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {
				Logger.debug("Reserving for customer with id -" +customerId);
				
				if (nesting) {
					ArrayList<String> carIds = new ArrayList<String>();
					ArrayList<String> flightIds = new ArrayList<String>();
					ArrayList<String> hotelIds = new ArrayList<String>();
					for (int j = 0; j < objIds.size(); j++) {
						String curr = objIds.get(j); 
						if (curr.contains("car")) {
							carIds.add(curr);
						} else if (curr.contains("flight")) {
							flightIds.add(curr);
						} else {
							hotelIds.add(curr);
						}
					}
					reservation(customerId, carIds, context);
					reservation(customerId, flightIds, context);
					reservation(customerId, hotelIds, context);
				} else {
					reservationWork(customerId, objIds, context,3);
				}	
				
				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return;
				}

			}  catch (AbortException e) {
				e.touch();
				Logger.debug("Exception in root transaction "+e.getTransaction());
			}
		}
	}

	static void reservation(String customerId, ArrayList<String> objIds, Context parentContext) throws AbortException{
		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsInner(i);

			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
//			context.setDescription("transfer from "+accountNum1+" account to "+accountNum2);
			context.sanityNested();

			Logger.debug("-----Started ----- W Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {
				
				reservationWork(customerId, objIds, context, 1);
				
				context.mergeParent();
				return;

			} catch (AbortException e) {			
				if (e.getTransaction().longValue() != context.getContextId().longValue()) {
					Logger.debug("Exception in parent transaction "+e.getTransaction());
					throw e;
				} else {
					Logger.debug("Exception in current transaction "+e.getTransaction());
				}
			}
		}
		
	}
	
	static void reservationWork(String customerId, ArrayList<String> objIds, Context context, int types) throws AbortException{

		ArrayList<Integer> maxPrices = new ArrayList<Integer>();
		for (int i = 0; i < types; i++) {
			maxPrices.add(new Integer(-1));
		}
		
		ArrayList<String> maxIds = new ArrayList<String>();
		for (int i = 0; i < types; i++) {
			maxIds.add(null);
		}

		Logger.debug("Checking for customer with id -" +customerId);
		Customer customer = (Customer)TMProxy.getTMProxy().read(customerId, context.getContextId());

		for (int i = 0; i < objIds.size(); i++) {
			Travel travelItem = (Travel)TMProxy.getTMProxy().read(objIds.get(i), context.getContextId());
			int price = -1;
			int type;
			
			if (types == 1) {
				type = 0;
			} else {
				 type = getType(travelItem.getId());
			}

			if (travelItem.isAvailable()) {
				price = travelItem.getPrice();
			}

			if (price > maxPrices.get(type)) {
				maxPrices.set(type, price);
				maxIds.set(type, travelItem.getId());
			}
		}

		for (int i = 0; i < types; i++) {

			if (maxIds.get(i) != null) {
				Travel travelItem = (Travel)TMProxy.getTMProxy().write(maxIds.get(i), context.getContextId());
				customer = (Customer)TMProxy.getTMProxy().write(customerId, context.getContextId());
				boolean added = customer.addReservation(travelItem);
				if (added) {
					travelItem.reserve();
					Logger.debug(travelItem.getId() +" is reserved for"+customerId);
				} {
					Logger.debug(travelItem.getId() +" is already presented");
				}
			} else {
				Logger.debug("no is not available");
			}
		}
	}

	public static void checkReservation(String customer) {

		Context parentContext = null;
		Long priority = System.currentTimeMillis();

		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsRoot(i);
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("starting root transaction for check reservation");
			context.sanityNested();
			context.setPriority(priority);

			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {
				Logger.debug("Checking for customer with id -" +customer);
				Customer c = (Customer)TMProxy.getTMProxy().read(customer, context.getContextId());
				c.checkReservations();

				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return;
				}

			}  catch (AbortException e) {
				e.touch();
				Logger.debug("Exception in root transaction "+e.getTransaction());
			}
		}

	}

	
	static void updateWork(ArrayList<String> objIds, Context context) throws AbortException {

		Random r = new Random();
		for (int i = 0; i < objIds.size(); i++) {
			Travel travelItem = (Travel)TMProxy.getTMProxy().write(objIds.get(i), context.getContextId());
			
			if (r.nextInt(100) > 50) {
				travelItem.increment();
			} else {
				travelItem.decrement();
			}
		}
	}

	
	public static void UpdateTravelItem(ArrayList<String> objIds) {
		Context parentContext = null;
		Long priority = System.currentTimeMillis();

		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsRoot(i);
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("starting root transaction for check reservation");
			context.sanityNested();
			context.setPriority(priority);

			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {

				updateWork(objIds, context);

				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return;
				}

			}  catch (AbortException e) {
				e.touch();
				Logger.debug("Exception in root transaction "+e.getTransaction());
			}
		}
	}

	public static int getType(String s) {
		if (s.contains("car")) {
			return 0;
		} else if (s.contains("flight")) {
			return 1;
		} else {
			return 2;
		}
	}

}
